// Quantitative Angiogram Analysis System
//
// Copyright ?Ping Zou, 2006
// sg71.cherub@gmail.com


using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
//using eee.Sheffield.PZ.Imaging;

namespace eee.Sheffield.PZ.QAAS
{
    public abstract class PZDraw : IPZDraw
    {
        #region Fields
        protected Graphics _graphic;
        protected double _maxX;
        protected double _minX;
        protected double _maxY;
        protected double _minY;
        protected double _height;
        protected double _width;
        protected PointF _original;
        protected object[] _sourceArray;

        protected Pen _bluePen = new Pen(Color.Blue);
        protected Pen _redPen = new Pen(Color.Red);
        protected Pen _greenPen = new Pen(Color.Green);
        protected Font _arialFont = new Font("Arial", 12);
        protected Brush _solidBlackBrush = new SolidBrush(Color.Black);
        protected Brush _solidRedBrush = new SolidBrush(Color.Red);

        private double _scaleX;
        private double _scaleY;
        private double _wOffset;
        private double _hOffset;
        #endregion

        #region Properties
        #endregion

        #region constructor
        protected PZDraw(Graphics g)
        {
            _graphic = g;
            _wOffset = 12.0;
            _hOffset = 12.0;
        }
        #endregion

        #region interface methods      
        /// <summary>
        /// set drawing area
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="maxX"></param>
        /// <param name="minX"></param>
        /// <param name="maxY"></param>
        /// <param name="minY"></param>
        public void Set(double width, double height, double maxX, double minX, double maxY, double minY)
        {
            _width = width;
            _height = height;
            _maxX = maxX;
            _minX = minX;
            _maxY = maxY;
            _minY = minY;
            _scaleX = GetScaleX();
            _scaleY = GetScaleY();
            _original = GetOriginal();
            DrawXAxis();
            DrawYAxis();
        } // Set()

        /// <summary>
        /// draw X axis
        /// </summary>
        public void DrawXAxis()
        {
            // draw axis
            float y = _original.Y;
            float xStart = (float) _wOffset;
            float xEnd = (float)(_width - 1.0);
            _graphic.DrawLine(_bluePen, new PointF(xStart, y), new PointF(xEnd, y));

            // draw arrow
            DrawArrow(_graphic, _bluePen, new PointF(xEnd, y), true);
            
            // label original
            _graphic.DrawString("0", _arialFont, _solidBlackBrush, new PointF(_original.X + 12.0F, _original.Y + 12.0F));
        } // DrawXAxis()

        public void DrawYAxis()
        {
            // draw axis
            float x = _original.X;
            float yStart = (float)(_height - _hOffset);
            float yEnd = 1;
            _graphic.DrawLine(_bluePen, new PointF(x, yStart), new PointF(x, yEnd));

            // draw arrow
            DrawArrow(_graphic, _bluePen, new PointF(x, yEnd), false);

            // label

        } // DrawYAxis()

        /// <summary>
        /// draw arrow, x direction (point right) or y direction (point up)
        /// </summary>
        /// <param name="g"></param>
        /// <param name="pen"></param>
        /// <param name="endPoint"></param>
        /// <param name="isXAxis"></param>
        private void DrawArrow(Graphics g, Pen pen, PointF endPoint, bool isXAxis)
        {
            float arrowLength = 5.0F;
            float arrowHalfAngle = 15.0F * (float)System.Math.PI / 180.0F; // radian
            float arrowHalfHeight = arrowLength * (float)System.Math.Tan(arrowHalfAngle);
            if (isXAxis)
                // x axis arrow, point right
            {
                g.DrawLine(pen, endPoint, new PointF(endPoint.X - arrowLength, endPoint.Y - arrowHalfHeight));
                g.DrawLine(pen, endPoint, new PointF(endPoint.X - arrowLength, endPoint.Y + arrowHalfHeight));
            }
            else
                // y axis arrow, point up
            {
                g.DrawLine(pen, endPoint, new PointF(endPoint.X - arrowHalfHeight, endPoint.Y + arrowLength));
                g.DrawLine(pen, endPoint, new PointF(endPoint.X + arrowHalfHeight, endPoint.Y + arrowLength));
            }
        } // DrawArrow()

        public abstract void Draw();

        public abstract void SetSource(object[] sourceArray);
        #endregion

        #region coordinate transform method
        /// <summary>
        /// get scaleX = (W - 2 * wOffset) / (maxX - minX)
        /// </summary>
        /// <returns></returns>
        private double GetScaleX()
        {
            if (_width == 0 && _maxX == 0 && _minX == 0)
                throw new ApplicationException("PZDraw::GetScaleX(), width, maxX or minX has not been initialized.");
            double scaleX = (_width - 2.0 * _wOffset) / (_maxX - _minX);
            return scaleX;
        } // GetScaleX()

        /// <summary>
        /// get scaleY = (H - 2 * hOffset) / (maxY - minY)
        /// </summary>
        /// <returns></returns>
        private double GetScaleY()
        {
            if (_height == 0 && _maxY == 0 && _minY == 0)
                throw new ApplicationException("PZDraw::GetScaleY(), height, maxY or minY has not been initialized.");
            double scaleY = (_height - 2.0 * _hOffset) / (_maxY - _minY);
            return scaleY;
        } // GetScaleY()

        /// <summary>
        /// get graph coordinate of the orginal point
        /// </summary>
        /// <returns></returns>
        private PointF GetOriginal()
        {
            if (_width == 0 && _maxX == 0 && _minX == 0 && _height == 0 && _maxY == 0 && _minY == 0)
                throw new ApplicationException("PZDraw::GetOriginal(), region geometric parameters have not been initialized.");

            float xr = _minX < 0 ? 0 : (float)_minX;
            float yr = _minY < 0 ? 0 : (float)_minY;
            PointF origional = GetGraphCoordinate(new PointF(xr, yr));
            return origional;
        } // GetOriginal()

        /// <summary>
        /// get coordinate on the graph region
        /// x = (xr - minX) * scaleX + wOffset
        /// y = H - hOffset - (yr - minY) * scaleY
        /// </summary>
        /// <param name="real"></param>
        /// <returns></returns>
        protected PointF GetGraphCoordinate(PointF real)
        {
            float x = (float)((real.X - _minX) * _scaleX + _wOffset);
            float y = (float)(_height - _hOffset - (real.Y - _minY) * _scaleY);
            return new PointF(x, y);
        } // GetGraphCoordinate()
        #endregion
    }
}
